#include <iostream>
#include <sstream>
#include <ruby.h>
#include "m3g/m3g.hpp"
#include "ruby-m3g.hpp"
using namespace m3g;
using namespace std;

namespace {
    struct ValidRangeAccessor {
        KeyframeSequence* keyframe_sequence;
    };
    struct KeyframeAccessor {
        KeyframeSequence* keyframe_sequence;
    };
    VALUE rb_cKeyframeSequence_ValidRangeAccessor;
    VALUE rb_cKeyframeSequence_KeyframeAccessor;
}

void ruby_KeyframeSequence_mark (KeyframeSequence* ptr)
{
    vector<Object3D*> objs;
    extract_all_references (ptr, objs);
    for (int i = 0; i < (int)objs.size(); i++) {
        VALUE val_obj = (VALUE)objs[i]->getExportedEntity();
        if (val_obj)
            rb_gc_mark (val_obj);
    }
}

void ruby_KeyframeSequence_free (KeyframeSequence* ptr)
{
    if (valid_ptr((unsigned char*)ptr, sizeof(*ptr))) {
        __TRY__;
        ptr->~KeyframeSequence ();
        __CATCH__;
    }
    ruby_xfree (ptr);
}

VALUE ruby_KeyframeSequence_allocate (VALUE self)
{
    void* p = ruby_xmalloc (sizeof(KeyframeSequence));
    return Data_Wrap_Struct (self, ruby_KeyframeSequence_mark, ruby_KeyframeSequence_free, p);
}

VALUE ruby_KeyframeSequence_initialize (VALUE self, VALUE val_num_keyframes, VALUE val_num_components, int val_interpolation)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    int num_keyframes  = NUM2INT(val_num_keyframes);
    int num_components = NUM2INT(val_num_components);
    int interpolation  = NUM2INT(val_interpolation);
    __TRY__;
    new (p) KeyframeSequence (num_keyframes, num_components, interpolation);
    __CATCH__;
    p->setExportedEntity ((void*)self);
    return self;
}

VALUE ruby_KeyframeSequence_get_component_count (VALUE self)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    int component_count;
    __TRY__;
    component_count = p->getComponentCount();
    __CATCH__;
    return INT2NUM(component_count);
}

VALUE ruby_KeyframeSequence_get_duration (VALUE self)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    int duration;
    __TRY__;
    duration = p->getDuration();
    __CATCH__;
    return INT2NUM(duration);
}

VALUE ruby_KeyframeSequence_get_interpolation_type (VALUE self)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    int type;
    __TRY__;
    type = p->getInterpolationType();
    __CATCH__;
    return INT2NUM(type);
}

VALUE ruby_KeyframeSequence_get_keyframe (VALUE self)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    KeyframeAccessor* accessor;
    VALUE         val_accessor = Data_Make_Struct (rb_cKeyframeSequence_KeyframeAccessor, KeyframeAccessor, 0, -1, accessor);
    accessor->keyframe_sequence = p;
    return val_accessor;
}

VALUE ruby_KeyframeSequence_get_keyframe_count (VALUE self)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    int keyframe_count;
    __TRY__;
    keyframe_count = p->getKeyframeCount();
    __CATCH__;
    return INT2NUM(keyframe_count);
}

VALUE ruby_KeyframeSequence_get_repeat_mode (VALUE self)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    int repeat;
    __TRY__;
    repeat = p->getRepeatMode();
    __CATCH__;
    return INT2NUM(repeat);
}


VALUE ruby_KeyframeSequence_get_valid_range (VALUE self)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    ValidRangeAccessor* accessor;
    VALUE           val_accessor = Data_Make_Struct (rb_cKeyframeSequence_ValidRangeAccessor, ValidRangeAccessor, 0, -1, accessor);
    accessor->keyframe_sequence = p;
    return val_accessor;
}

VALUE ruby_KeyframeSequence_set_duration (VALUE self, VALUE val_duration)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    int duration = NUM2INT(val_duration);
    __TRY__;
    p->setDuration (duration);
    __CATCH__;
    return Qnil;
}

VALUE ruby_KeyframeSequence_set_repeat_mode (VALUE self, VALUE val_mode)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    int mode = NUM2INT(val_mode);
    __TRY__;
    p->setRepeatMode (mode);
    __CATCH__;
    return Qnil;
}

VALUE ruby_KeyframeSequence_set_valid_range (VALUE self, VALUE val_valid_range)
{
    rb_check_type (val_valid_range, T_ARRAY);
    if (RARRAY_LEN(val_valid_range) != 2) {
        rb_raise (rb_eArgError, "wrong array length %d (expected 2)", RARRAY_LEN(val_valid_range));
    }
    VALUE val_first = rb_ary_entry(val_valid_range, 0);
    VALUE val_last  = rb_ary_entry(val_valid_range, 1);
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    int first = NUM2INT (val_first);
    int last  = NUM2INT (val_last);
    __TRY__;
    p->setValidRange (first, last);
    __CATCH__;
    return Qnil;
}

VALUE ruby_KeyframeSequence_print (VALUE self)
{
    KeyframeSequence* p;
    Data_Get_Struct (self, KeyframeSequence, p);
    ostringstream out;
    __TRY__;
    p->print (out);
    __CATCH__;
    return rb_str_new2 (out.str().c_str());
}

/**
 * KeyframeSequence:: ValidRangeAccessor
 */

VALUE ruby_KeyframeSequence_ValidRangeAccessor_get_first (VALUE self)
{
    ValidRangeAccessor* p;
    Data_Get_Struct (self, ValidRangeAccessor, p);
    int first;
    __TRY__;
    first = p->keyframe_sequence->getValidRangeFirst();
    __CATCH__;
    return INT2NUM(first);
}

VALUE ruby_KeyframeSequence_ValidRangeAccessor_get_last (VALUE self)
{
    ValidRangeAccessor* p;
    Data_Get_Struct (self, ValidRangeAccessor, p);
    int last;
    __TRY__;
    last = p->keyframe_sequence->getValidRangeLast();
    __CATCH__;
    return INT2NUM(last);
}

/**
 * KeyframeSequence:: KeyframeAccessor
 */

VALUE ruby_KeyframeSequence_KeyframeAccessor_get_keyframe (VALUE self, VALUE val_index)
{
    KeyframeAccessor* p;
    Data_Get_Struct (self, KeyframeAccessor, p);
    int index    = NUM2INT(val_index);
    int component_count = p->keyframe_sequence->getComponentCount ();
    float* value = (float*)ruby_xmalloc(sizeof(float)*component_count);
    int    time;
    __TRY__;
    time = p->keyframe_sequence->getKeyframe (index, value);
    __CATCH__;
    VALUE val_value = rb_ary_new2 (component_count);
    for (int i = 0; i < component_count; i++) {
        rb_ary_push (val_value, rb_float_new(value[i]));
    }
    ruby_xfree (value);
    VALUE val_ret = rb_ary_new2 (2);
    rb_ary_push (val_ret, INT2NUM(time));
    rb_ary_push (val_ret, val_value);
    return val_ret;
}

VALUE ruby_KeyframeSequence_KeyframeAccessor_set_keyframe (VALUE self, VALUE val_index, VALUE val_keyframe)
{
    rb_check_type (val_keyframe, T_ARRAY);
    if (RARRAY_LEN(val_keyframe) != 2) {
        rb_raise (rb_eArgError, "wrong array length %d (expected 2)", RARRAY_LEN(val_keyframe));
    }
    KeyframeAccessor* p;
    Data_Get_Struct (self, KeyframeAccessor, p);
    VALUE val_time        = rb_ary_entry(val_keyframe, 0);
    VALUE val_value       = rb_ary_entry(val_keyframe, 1);
    int   component_count = p->keyframe_sequence->getComponentCount();
    rb_check_type (val_value, T_ARRAY);
    if (RARRAY_LEN(val_value) < component_count) {
        rb_raise (rb_eArgError, "wrong array length %d (expected %d)", RARRAY_LEN(val_value), component_count);
    }

    int    index  = NUM2INT(val_index);
    int    time   = NUM2INT(val_time);
    float* value = (float*)ruby_xmalloc(sizeof(float)*component_count);
    for (int i = 0; i < component_count; i++) {
        value[i] = NUM2DBL (rb_ary_entry(val_value, i));
    }
    __TRY__;
    p->keyframe_sequence->setKeyframe (index, time, value);
    __CATCH__;
    ruby_xfree (value);
  
    return Qnil;
}


/**
 *
 */
void register_KeyframeSequence ()
{
    // KeyframeSequence
    rb_cKeyframeSequence    = rb_define_class_under (rb_mM3G, "KeyframeSequence",    rb_cObject3D);

    rb_define_const (rb_cKeyframeSequence, "CONSTANT", INT2NUM(KeyframeSequence::CONSTANT));
    rb_define_const (rb_cKeyframeSequence, "LOOP",     INT2NUM(KeyframeSequence::LOOP));
    rb_define_const (rb_cKeyframeSequence, "LINEAR",   INT2NUM(KeyframeSequence::LINEAR));
    rb_define_const (rb_cKeyframeSequence, "SLERP",    INT2NUM(KeyframeSequence::SLERP));
    rb_define_const (rb_cKeyframeSequence, "SPLINE",   INT2NUM(KeyframeSequence::SPLINE));
    rb_define_const (rb_cKeyframeSequence, "SQUAD",    INT2NUM(KeyframeSequence::SQUAD));
    rb_define_const (rb_cKeyframeSequence, "STEP",     INT2NUM(KeyframeSequence::STEP));

    rb_define_alloc_func     (rb_cKeyframeSequence, ruby_KeyframeSequence_allocate);
    rb_define_private_method (rb_cKeyframeSequence, "initialize", (VALUE(*)(...))ruby_KeyframeSequence_initialize, 3);

    rb_define_method (rb_cKeyframeSequence, "component_count",     (VALUE(*)(...))ruby_KeyframeSequence_get_component_count,    0);
    rb_define_method (rb_cKeyframeSequence, "duration",            (VALUE(*)(...))ruby_KeyframeSequence_get_duration,           0);
    rb_define_method (rb_cKeyframeSequence, "interpolation_type",  (VALUE(*)(...))ruby_KeyframeSequence_get_interpolation_type, 0);
    rb_define_method (rb_cKeyframeSequence, "keyframe",            (VALUE(*)(...))ruby_KeyframeSequence_get_keyframe,           0);
    rb_define_method (rb_cKeyframeSequence, "keyframe_count",      (VALUE(*)(...))ruby_KeyframeSequence_get_keyframe_count,     0);
    rb_define_method (rb_cKeyframeSequence, "repeat_mode",         (VALUE(*)(...))ruby_KeyframeSequence_get_repeat_mode,        0);
    rb_define_method (rb_cKeyframeSequence, "valid_range",         (VALUE(*)(...))ruby_KeyframeSequence_get_valid_range,        0);
    rb_define_method (rb_cKeyframeSequence, "duration=",           (VALUE(*)(...))ruby_KeyframeSequence_set_duration,           1);
    rb_define_method (rb_cKeyframeSequence, "repeat_mode=",        (VALUE(*)(...))ruby_KeyframeSequence_set_repeat_mode,        1);
    rb_define_method (rb_cKeyframeSequence, "valid_range=",        (VALUE(*)(...))ruby_KeyframeSequence_set_valid_range,        1);
    rb_define_method (rb_cKeyframeSequence, "to_s",                (VALUE(*)(...))ruby_KeyframeSequence_print,                  0);


    // KeyframeSequence:: ValidRangeAccessor
    rb_cKeyframeSequence_ValidRangeAccessor    = rb_define_class_under (rb_cKeyframeSequence, "ValidRangeAccessor",    rb_cObject);

    rb_define_method (rb_cKeyframeSequence_ValidRangeAccessor, "first",      (VALUE(*)(...))ruby_KeyframeSequence_ValidRangeAccessor_get_first, 0);
    rb_define_method (rb_cKeyframeSequence_ValidRangeAccessor, "last",       (VALUE(*)(...))ruby_KeyframeSequence_ValidRangeAccessor_get_last,  0);

    // KeyframeSequence:: KeyframeAccessor
    rb_cKeyframeSequence_KeyframeAccessor    = rb_define_class_under (rb_cKeyframeSequence, "KeyframeAccessor",    rb_cObject);

    rb_define_method (rb_cKeyframeSequence_KeyframeAccessor, "[]",       (VALUE(*)(...))ruby_KeyframeSequence_KeyframeAccessor_get_keyframe,   1);
    rb_define_method (rb_cKeyframeSequence_KeyframeAccessor, "[]=",      (VALUE(*)(...))ruby_KeyframeSequence_KeyframeAccessor_set_keyframe,   2);
}

